---
title: 'Next.js Redirect Tanpa Flashing Content'
publishedAt: '2021-05-18'
description: 'Next.js secara default mengirim static site, maka redirecting unauthenticated user kadang bisa menjadi problem.'
banner: 'piermanuele-sberni-cLRUmTnN7_c-unsplash_lstsd5'
tags: 'nextjs'
---

## Introduction

> Next.js melakukan prerender untuk static page, kemudian baru melakukan hydrate ke interaktivitas yang full secara client side.

Dengan konsep tersebut, maka kita akan melihat halaman HTML dan CSS terlebih dahulu, kemudian beberapa detik kemudian, kita mendapatkan JavaScript, dan semua interaktivitas seperti click button.

## The Problem

Pada Create React App, melakukan redirect atau `history.push` bukan sebuah masalah, karena semua data difetch pada client-side, sehingga kita akan mendapatkan file lengkap dengan Javascriptnya. Flashing content otomatis tidak menjadi masalah.

Tetapi, di Next.js kita mendapatkan static page, kemudian setelah hydration, code JavaScript untuk redirect baru akan berjalan. Ini menjadi masalah karena akan ada content yang muncul untuk beberapa detik sebelum aplikasi di redirect. Hal ini tentunya tidak diinginkan terutama untuk konten yang tidak boleh dilihat oleh unauthorized user.

Saya melihat problem ini masih ada beberapa di production app, beberapa memang menutupi data" penting karena biasanya di fetch secara client-side, tetapi kadang shell dari kontennya masih muncul untuk waktu sebentar. Coba buka website ini [app.splitbee.io/projects](https://app.splitbee.io/projects/theodorusclarence.com). Kamu seharusnya tidak memiliki akses ke website ini, tetapi karena problem tersebut, kita bisa melihat dashboard shell muncul sebentar, baru diarahkan ke /login page

<CloudinaryImg
  mdx
  publicId='theodorusclarence/blogs/nextjs-redirect-no-flashing/splitbee_ck84xh'
  alt='splitbee'
  width={1590}
  height={896}
/>

Sebenarnya ada cukup banyak jawaban di internet untuk menggunakan method seperti server-side rendering, dan memanfaatkan cookies dengan menggunakan `dangerouslySetInnerHTML` di Head tag.

Method yang saya gunakan ini tidak perlu hal" tersebut, tetapi kita wajib menggunakan full-page loader untuk menutupi kontennya.

## Solution

Saya membuat demo di [https://learn-auth-redirect-nextjs.vercel.app/](https://learn-auth-redirect-nextjs.vercel.app/)

Anda bisa mencoba membuka page tersebut, dan langsung mengunjungi [learn-auth-redirect-nextjs.vercel.app/blocked](http://learn-auth-redirect-nextjs.vercel.app/blocked) di new tab. Kamu akan secara singkat melihat loader, kemudian diredirect ke '/' tanpa melihat konten di dalamnya sama sekali.

Ada 2 approach yang saya temukan.

### 1. Melakukan checking di setiap page

```jsx
import { useEffect } from 'react';
import { useRouter } from 'next/router';
import { useAuth } from '@/contexts/auth';
import FullPageLoader from '@/components/FullPageLoader';

export default function blocked() {
  const router = useRouter();

  const { isAuthenticated, user, logout, isLoading } = useAuth();

  useEffect(() => {
    if (!isLoading && !isAuthenticated) {
      router.push('/');
    }
  }, [isAuthenticated, isLoading]);

  if (isLoading || !isAuthenticated) {
    return <FullPageLoader />;
  }

  return (
    <div className='layout space-y-4 py-12'>
      <h1>YOUR CONTENT THAT SHOULD NOT BE SEEN UNLESS AUTHENTICATED</h1>
    </div>
  );
}
```

Disini, kita mendapatkan nilai `isAuthenticated` dari auth context, kamu bisa [melihat repositori](https://github.com/theodorusclarence/learn-auth-redirect-nextjs) untuk melihat code lebih lengkap.

Dari code diatas, akan mereturn FullPageLoader componet terlebih dahulu sambil menunggu page render, dan menunggu auth context selesai mengecek token ke API menggunakan useEffect.

Potongan code ini menggunakan useEffect di authentication context, untuk memverifikasi token yang biasanya disimpan di localStorage. Jika kamu ingin mengetahu Authentication Context pattern lebih lanjut, saya memiliki [code snippet](http://theodorusclarence.com/library/auth-context) tentang itu.

Context juga mengembalikan isLoading value, dan kita menampilkan loader ketika sedang loading, sampai kita mendapat value isAuthenticatednya

Pattern ini akan secara efektif memblock content yang tidak ingin kita tampilkan ke unauthorized users. Tetapi, dengan menggunakan first approach ini, akan menyulitkan jika kita harus menambahkan pattern useEffect pada setiap pagenya. Maka saya membuat component PrivateRoute, mirip seperti React Router patternnya CRA.

### 2. Menggunakan PrivateRoute Component

```jsx
import { useEffect } from 'react';
import { useRouter } from 'next/router';

import { useAuth } from '@/contexts/auth';
import FullPageLoader from './FullPageLoader';

export default function PrivateRoute({ protectedRoutes, children }) {
  const router = useRouter();
  const { isAuthenticated, isLoading } = useAuth();

  const pathIsProtected = protectedRoutes.indexOf(router.pathname) !== -1;

  useEffect(() => {
    if (!isLoading && !isAuthenticated && pathIsProtected) {
      // Redirect route, you can point this to /login
      router.push('/');
    }
  }, [isLoading, isAuthenticated, pathIsProtected]);

  if ((isLoading || !isAuthenticated) && pathIsProtected) {
    return <FullPageLoader />;
  }

  return children;
}
```

Dengan menggunakan komponen ini, kita bisa menyantumkan route yang mau kita protect di \_app.js

```jsx
//_app.js

import SEO from '@/next-seo.config';
import '@/styles/globals.css';
import { AuthProvider } from '@/contexts/auth';
import PrivateRoute from '@/components/PrivateRoute';

function MyApp({ Component, pageProps }) {
  // Add your protected routes here
  const protectedRoutes = ['/blocked-component'];

  return (
    <>
      <AuthProvider>
        <PrivateRoute protectedRoutes={protectedRoutes}>
          <Component {...pageProps} />
        </PrivateRoute>
      </AuthProvider>
    </>
  );
}

export default MyApp;
```

> I'm open for all suggestions and contributions to improve 🚀. Open a PR on the repository or email me at me@theodorusclarence.com

## Demo

### 1. Tanpa menggunakan full page loader & not authenticated `/blocked-unhandled`

<CloudinaryImg
  mdx
  publicId='theodorusclarence/blogs/nextjs-redirect-no-flashing/blocked-unhandled_m4chdj'
  alt='blocked-unhandled'
  width={1440}
  height={852}
/>

Dapat kita lihat, tulisan text merah masih bisa kita lihat.

### 2. Menggunakan full page loader & not authenticated `/blocked-component`

<CloudinaryImg
  mdx
  publicId='theodorusclarence/blogs/nextjs-redirect-no-flashing/blocked_nbwdvm'
  alt='blocked'
  width={1440}
  height={852}
/>

Dengan menggunakan full page loader, maka tidak ada konten yang akan ke flash

### 3. Menggunakan full page loader & authenticated dengan cara mengecek token localStorage

<CloudinaryImg
  mdx
  publicId='theodorusclarence/blogs/nextjs-redirect-no-flashing/blocked-token_lhresk'
  alt='blocked-token'
  width={1440}
  height={852}
/>

Menggunakan full page loader akan tetap berjalan ketika user sudah memiliki token
di localStorage
